/**
 * \file Timer.cc
 * \brief Timer facilities
 * \author Corey Tabaka
 */

/*
   Copyright 2006 Corey Tabaka

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

#include <platform/Timer.h>
#include <platform/Pit.h>
#include <platform/Interrupt.h>
#include <platform/msr.h>
#include <Sync.h>

#include <i386/console.h>
#include <stdio.h>

/*
 * System time.
 */
static timestamp_t sysTime;

/*
 * Interrupt mutex.
 */
static InterruptLock timerLock;

extern uint64 cpuTicksPerNano;
extern uint64 cpuTicksPerMicro;
extern uint64 cpuTicksPerSec;

static bool interruptHandler(regs_t *regs);

void Timer::initialize(void) {
	Interrupt::registerHandler(INT_PIT, interruptHandler, 1000);
		
	printf("        Rate set to %u Hz\n", Pit::getFrequency());
}

uint32 Timer::getFrequency(void) {
	return Pit::getFrequency();
}

uint64 Timer::getTicks(void) {
	return sysTime.value;
}

uint32 Timer::getMillis(void) {
	return (uint32) (sysTime.value * 1000 / Pit::getFrequency());
}

uint64 Timer::getCpuTicksPerSec(void) {
	return cpuTicksPerSec;
}

uint64 Timer::getCpuTicksPerMicro(void) {
	return cpuTicksPerMicro;
}

uint64 Timer::getCpuTicksPerNano(void) {
	return cpuTicksPerNano;
}

uint64 Timer::getMicroTime(void) {
	timestamp_t now;
	rdtsc(now.low, now.high);
	
	return now.value / cpuTicksPerMicro;
}

/*
 * Measure cpu speed using Intel's reccomended procedure with rdtsc. High-
 * precision timers are derived from this calibration.
 */
void Timer::calibrateTimers(void) {
	printf("    Calibrating timers\n");
	
	uint32 tstart = getMillis();
	uint32 tstop;
	timestamp_t cstart, cend;
	
	for (;;) {
		tstop = getMillis();
		
		if ((int) (tstop - tstart) > 0) {
			rdtsc(cstart.low, cstart.high);
			break;
		}
	}
	
	for (;;) {
		tstop = getMillis();
		
		if ((int) (tstop - tstart) > 1000) {
			rdtsc(cend.low, cend.high);
			break;
		}
	}
	
	uint32 elapsedTime = tstop - tstart;
	uint64 diff = cend.value - cstart.value;
	
	cpuTicksPerNano = diff / (elapsedTime * 1000000);
	cpuTicksPerMicro = diff / (elapsedTime * 1000);
	cpuTicksPerSec = diff / (elapsedTime / 1000);
	
	//printf("        Ticks per nanosecond: %lld\n", cpuTicksPerNano);
	printf("        Ticks per microsecond: %lld\n", cpuTicksPerMicro);
	printf("        Ticks per second: %llu\n", cpuTicksPerSec);
}

static bool interruptHandler(regs_t *regs) {
	sysTime.value++;
	
	printf_xy(71, 0, WHITE, "%08x", sysTime.low);
	
	return true;
}
